package server;

import java.io.BufferedReader;
import javax.naming.ldap.LdapName;
import javax.naming.ldap.Rdn;
import shared.CallObject;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.PrintWriter;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.cert.CertificateException;
import java.util.ArrayList;

import javax.net.ServerSocketFactory;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLServerSocket;
import javax.net.ssl.SSLServerSocketFactory;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
import javax.security.cert.X509Certificate;

import com.mysql.jdbc.UpdatableResultSet;

import shared.CallObject;

public class Server {

	private static CallObject recieveObject;
	private static CallObject sendObject;
	private static String clientDivision;
	private static String clientTitle;
	private static String clientName;
	private static Database db;

	public Server() throws IOException {

	}

	public static void main(String[] args) {
		int port = 1050;

		db = new Database();
		db.openConnection();

		SSLServerSocketFactory socketFactory = getServerSocket();
		SSLServerSocket serverSocket;

		try {
			serverSocket = (SSLServerSocket) socketFactory
					.createServerSocket(port);
			serverSocket.setNeedClientAuth(true);

			SSLSocket sslSocket = (SSLSocket) serverSocket.accept();
			SSLSession session = sslSocket.getSession();
			
			X509Certificate clientCert = (X509Certificate) session
					.getPeerCertificateChain()[0];
			String clientSubject = clientCert.getSubjectDN().getName();
			System.out.println("clientSubject: " + clientSubject);
			String[] tempCNName = clientSubject.split(", ");
			clientName = tempCNName[0].substring(3);
			clientName = clientName.replaceAll(" ", "");
			clientName = clientName.toLowerCase();
			updateLog(clientName + " has logged in");
			clientTitle = tempCNName[1].substring(3);
			clientDivision = tempCNName[3].substring(2);

			ObjectInputStream reader = new ObjectInputStream(
					sslSocket.getInputStream());
			ObjectOutputStream writer = new ObjectOutputStream(
					sslSocket.getOutputStream());

			// THE SERVER STARTS TO SEND A MESSAGE TO THE CLIENT
			sendObject = new CallObject(0, "Welcome!", null);
			writer.writeObject(sendObject);

			do {
				recieveObject = (CallObject) reader.readObject();
				if (recieveObject != null) {
					// TODO: något vi vill göra med recieveObject
					sendObject = exe(recieveObject);
					// CREATES AND SENDS A NEW CALLOBJECT
					writer.writeObject(sendObject);
				}
			} while (recieveObject != null); // TODO: INTE BRA..
			sslSocket.close();

		} catch (IOException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}

	}

	private static SSLServerSocketFactory getServerSocket() {
		char[] passphrase = "server".toCharArray(); // lösenord för keystore
		String password = "server";
		String filename = "/h/d3/x/ada10man/EDA040-Project/Hospital/Server/";

		try {
			System.setProperty("javax.net.ssl.trustStore", filename);
			System.setProperty("javax.net.ssl.trustStorePassword", password);

			KeyStore ks;
			ks = KeyStore.getInstance("JKS");
			ks.load(new FileInputStream(filename + "server.jks"), passphrase);

			KeyManagerFactory kmf;
			kmf = KeyManagerFactory.getInstance("SunX509");
			kmf.init(ks, passphrase);

			KeyStore ts;
			ts = KeyStore.getInstance("JKS");
			ts.load(new FileInputStream(filename + "server.ts"), passphrase);

			TrustManagerFactory tmf;
			tmf = TrustManagerFactory.getInstance("SunX509");
			tmf.init(ts);

			SSLContext context;
			context = SSLContext.getInstance("TLS");
			context.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);

			return context.getServerSocketFactory();

		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}

	private static CallObject exe(CallObject recieveObject) {
		int command = recieveObject.getCommand();
		sendObject = new CallObject(0, null, null);
		switch (command) {
		case CallObject.VIEW_RECORDS:

			ArrayList<String> nbrList = db.getPersonRecords(clientName,
					clientTitle, clientDivision);
			System.out.println("CLIENT NAME: " + clientName
					+ ", CLIENT TITLE: " + clientTitle + ", CLIENT DIV: "
					+ clientDivision);
			System.out.println("nbrList.size= " + nbrList.size());

			StringBuilder sb = new StringBuilder();
			StringBuilder logbuilder = new StringBuilder();

			for (int i = 0; i < nbrList.size(); i++) {
				logbuilder.append(db.getInfo(nbrList.get(i), "nbr") + ", ");

				String recordsInfo = "Record number: "
						+ db.getInfo(nbrList.get(i), "nbr") + " \n Patient: "
						+ db.getInfo(nbrList.get(i), "patient")
						+ " \n Doctor: " + db.getInfo(nbrList.get(i), "doctor")
						+ " \n Nurse: " + db.getInfo(nbrList.get(i), "nurse")
						+ " \n Division: "
						+ db.getInfo(nbrList.get(i), "division")
						+ " \n Information: "
						+ db.getInfo(nbrList.get(i), "information") + "\n \n";
				sb.append(recordsInfo);
			}
			sendObject.setText1(sb.toString());
			updateLog(clientName + " viewed records: " + logbuilder.toString());
			break;
		case CallObject.SAVE_RECORD:
			int recordNbr = Integer.parseInt(recieveObject.getText1());
			System.out.println("record number: " + recordNbr);
			if (db.writeTo(recordNbr, recieveObject.getText2(), clientName)) {
				sendObject.setText1("Record successfully saved");
				updateLog(clientName + " saved record: " + recordNbr);
			} else {
				sendObject.setText1("Record NOT saved");
				updateLog(clientName + " TRIED BUT FAILED TO saved record: "
						+ recordNbr);
			}
			break;

		case CallObject.CREATE_RECORD:
			if (clientTitle.compareTo("doctor") == 0) {
				if (db.createRecord(recieveObject.getText1(), clientName,
						recieveObject.getText2(), clientDivision)) {
					sendObject.setText1("New record successfully created");
					updateLog(clientName + " created new record");
				} else {
					sendObject.setText1("New record NOT created");
					updateLog(clientName
							+ " TRIED BUT FAILED TO create new record");
				}
			}
			break;

		case CallObject.DELETE_RECORD:
			if (clientName.compareTo("aggeagency") == 0
					&& db.removeRecord(Integer.parseInt(recieveObject
							.getText1()))) {
				sendObject.setText1("Record successfully deleted");
				updateLog(clientName + " deleted record: "
						+ recieveObject.getText1());
			} else {

				updateLog(clientName + " TRIED BUT FAILED TO deleted record: "
						+ recieveObject.getText1());
				sendObject.setText1("Record NOT deleted");

			}
		default:
			break;
		}
		return sendObject;
	}

	
	private static void updateLog(String info){
		db.updateLog(info);
	}
}

